class Vue {
  constructor() {}
}
/**
 * 双向绑定
 * 响应式系统的简单实现
 */

let activeEffectFn = null;
const activeEffectFnStack = [];
const bucket = new WeakMap();
let ownKeysSymbo = Symbol();
let TriggerType = {
  SET: "SET",
  ADD: "ADD",
  DELETE: "DELETE",
};
function track(target, key) {
  // 每个对象下面对应的属性都有一个副作用函数
  if (!activeEffectFn) return;
  let deps = bucket.get(target);
  if (!deps) {
    bucket.set(target, (deps = new Map()));
  }
  let effectFns = deps.get(key);
  if (!effectFns) {
    deps.set(key, (effectFns = new Set()));
  }
  effectFns.add(activeEffectFn);
  activeEffectFn.deps.push(effectFns);
}
function trigger(target, key, type) {
  let deps = bucket.get(target);
  console.log(type, "effectFns");
  if (!deps) return;
  let effectFns = deps.get(key);
  const efectsToRun = new Set(effectFns);
  let ownkeysEffect = deps.get(ownKeysSymbo);
  console.log(ownkeysEffect, "ownkeysEffect");
  if (type === TriggerType.ADD|| type === TriggerType.DELETE) {
    ownkeysEffect.forEach((effectFn) => {
      if (effectFn && effectFn !== activeEffectFn) {
        efectsToRun.add(effectFn);
      }
    });
  }
  efectsToRun.forEach((effectFn) => {
    if (effectFn && effectFn !== activeEffectFn) {
      if (effectFn.options && effectFn.options.schelder) {
        effectFn.options.schelder(effectFn);
      } else {
        effectFn();
      }
    }
  });
}
let handle = {
  get(target, key, receiver) {
    // 在get中进行track收集依赖函数
    track(target, key);
    return target[key];
  },
  set(target, key, newValue, receiver) {
    let type = Object.prototype.hasOwnProperty.call(target, key)
      ? TriggerType.SET
      : TriggerType.ADD;
    target[key] = newValue;
    trigger(target, key, type);
    return true;
  },
  has(target, key) {
    track(target, key);
    console.log(target, key);
    return Reflect.has(target, key);
  },
  ownKeys(target) {
    console.log(target);
    track(target, ownKeysSymbo);
    return Reflect.ownKeys(target);
  },
  deleteProperty(target, key) {
    let hasOwnKey = Object.prototype.hasOwnProperty.call(target, key)
    let isDelete = Reflect.deleteProperty(target, key)
    if(hasOwnKey && isDelete){
      trigger(target, key, TriggerType.DELETE)
    }
    return isDelete
  },
};
// 清除分支
function cleanup(activeFn) {
  for (let i = 0; i < activeFn.deps.length; i++) {
    let deps = activeFn.deps[i];
    deps.delete(activeFn);
  }
  activeFn.deps.length = 0;
}
function effect(effectFn, options) {
  function activeFn() {
    cleanup(activeFn);
    activeEffectFn = activeFn;
    activeEffectFnStack.push(activeFn);
    let res = effectFn();
    activeEffectFnStack.pop();
    activeEffectFn = activeEffectFnStack[activeEffectFnStack.length - 1];
    return res;
  }
  activeFn.deps = [];
  activeFn.options = options;
  if (options && options.lazy) {
    return activeFn;
  }
  activeFn();
}
function computed(getter) {
  let cache;
  let effectFn = effect(getter, {
    schelder() {
      cache = false;
      trigger(obj, "value");
    },
    lazy: true,
  });
  let res = effectFn();

  let obj = {
    get value() {
      if (!cache) {
        res = effectFn();
        cache = true;
        track(obj, "value");
        console.log("get");
      }
      return res;
    },
  };

  return obj;
}
let i = 2;
function ajax() {
  return new Promise(function (resolve, reject) {
    let num = i;
    console.log(num);
    setTimeout(() => {
      resolve(num);
    }, i * 1000);
    i--;
  });
}
function watcher(getter, callback, options) {
  let cleanup = null;
  function onInvalidatecleanup(fn) {
    cleanup = fn;
  }
  let oldValue;
  let effectFn = effect(getter, {
    schelder() {
      console.log("trigger");
      job();
    },
    lazy: true,
  });
  function job() {
    if (cleanup) cleanup();
    let value = effectFn();
    callback(value, oldValue, onInvalidatecleanup);
    oldValue = value;
  }
  if (options && options.immediate) {
    job();
  } else {
    oldValue = effectFn();
  }
}
let data = {
  name: "zs",
  age: 15,
  num: 1,
  ok: true,
};
let obj = new Proxy(data, handle);
effect(() => {
  for (let key in obj) {
    console.log(key);
  }
});
delete obj.name;
